package com.yanqu.road.server.manger.activity.businessreunion;

import com.yanqu.road.entity.activity.businessreunion.BusinessReunionData;
import com.yanqu.road.entity.activity.businessreunion.RecallUserInfo;
import com.yanqu.road.entity.enums.eLogMoneyType;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.logic.bussiness.activity.BusinessReunionBusiness;
import com.yanqu.road.server.TempMgr;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.activity.businessreunion.BusinessReunionActivityModule;
import com.yanqu.road.server.gameplayer.module.player.CurrencyModule;
import com.yanqu.road.server.manger.player.GamePlayerMgr;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.string.StringUtils;

import java.math.BigInteger;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ：zfr
 * @date ：2022/4/18 16:59
 */
public class BusinessReunionMgr extends TempMgr {
    /**
     * 玩家商海重逢数据
     */
    private static Map<Long, BusinessReunionData> dataMap = new ConcurrentHashMap<>();

    private static Map<Long, Object> lockMap = new ConcurrentHashMap<>();

    public static BusinessReunionData getBusinessReturnData(long userId) {
        return dataMap.get(userId);
    }

    public static void reloadActivityData(int activityId) {
        dataMap = BusinessReunionBusiness.getBusinessReunionDataMap(activityId);
    }

    public static void addData(BusinessReunionData businessReunionData) {
        synchronized (dataMap) {
            if (dataMap.containsKey(businessReunionData.getPlayerId())) {
                BusinessReunionData data = dataMap.get(businessReunionData.getPlayerId());
                if (data.getActivityId() == businessReunionData.getActivityId()) {
                    return;
                }
            }
            dataMap.put(businessReunionData.getPlayerId(), businessReunionData);
        }
    }

    private static Object getUserLock(long userId) {
        if (!lockMap.containsKey(userId)) {
            synchronized (lockMap) {
                if (!lockMap.containsKey(userId)) {
                    lockMap.put(userId, new Object());
                }
            }
        }
        return lockMap.get(userId);
    }

    /**
     * 召回绑定
     * @param userId
     * @param recallUserId
     * @param userBaseInfo
     * @param recallCode
     */
    public static int recallBind(long userId, long recallUserId, UserBaseInfo userBaseInfo, String recallCode) {
        BusinessReunionData businessReunionData = getBusinessReturnData(userId);
        if (businessReunionData == null) {
            return GameErrorCode.E_BUSINESS_REUNION_RECALL_CODE_ERROR; //召回码有误
        }
        if (!Objects.equals(businessReunionData.getRecallCode(), recallCode)) {
            return GameErrorCode.E_BUSINESS_REUNION_RECALL_CODE_ERROR; //召回码有误
        }
        RecallUserInfo recallUserInfo = getRecallUserInfo(businessReunionData, recallUserId);
        if (recallUserInfo != null) {
            long timeMillis = System.currentTimeMillis() - recallUserInfo.getRecallTime();
            //回归触发了多久 能否重置
            if (timeMillis / DateHelper.DAY_MILLIONS < BusinessReunionActivityMgr.getConfig().getReturnResetTime()) {
                return GameErrorCode.E_BUSINESS_REUNION_ALREADY_BIND; //已经填写过;
            }
        }
        recallUserInfo = initRecallUserInfo(userId, recallUserId, userBaseInfo);
        synchronized (getUserLock(userId)) {
            calBindCanAddIntegral(businessReunionData, recallUserInfo);
            calAddVipExpAddIntegral(businessReunionData, recallUserInfo);
            businessReunionData.addRecallUserInfo(recallUserInfo);
            addIntegral(businessReunionData);
        }
        return 0;
    }

    private static void calAddVipExpAddIntegral(BusinessReunionData businessReunionData, RecallUserInfo recallUserInfo) {
        if (recallUserInfo.getAddVipExp() <= 0) {
            return;
        }
        ReunionConfig config = BusinessReunionActivityMgr.getConfig();
        if (config == null) {
            return;
        }
        int addIntegral = 0;
        for (RecallUserInfo info : businessReunionData.getRecallUserInfos()) {
            if (info.getRecallUserId() != recallUserInfo.getRecallUserId()) {
                addIntegral += info.getAddVipIntegral();
            }
        }
        if (addIntegral >= config.getVipExpAddIntegralLimit()) {
            return;
        }
        int addVipExpAddIntegral = recallUserInfo.getAddVipExp() * config.getAddIntegralPerAddVipExp();
        recallUserInfo.setAddVipIntegral(Math.min(addVipExpAddIntegral, config.getVipExpAddIntegralLimit() - addIntegral));
    }

    private static void calBindCanAddIntegral(BusinessReunionData businessReunionData, RecallUserInfo recallUserInfo) {
        ReunionConfig config = BusinessReunionActivityMgr.getConfig();
        if (config == null) {
            return;
        }
        int recallAddIntegral = 0;
        for (RecallUserInfo recall : businessReunionData.getRecallUserInfos()) {
            recallAddIntegral += recall.getAddBindIntegral();
        }
        int diff = config.getRecallAddIntegralLimit() - recallAddIntegral;
        if (diff <= 0) {
            return;
        }
        recallUserInfo.setAddBindIntegral(recallUserInfo.getAddBindIntegral() + Math.min(config.getRecallOneAddIntegral(), diff));
    }


    private static RecallUserInfo initRecallUserInfo(long userId, long recallUserId, UserBaseInfo userBaseInfo) {
        RecallUserInfo recallUserInfo = new RecallUserInfo();
        recallUserInfo.setUserId(userId);
        recallUserInfo.setRecallServerId(userBaseInfo.getServerId());
        recallUserInfo.setRecallUserId(recallUserId);
        recallUserInfo.setUserBaseInfo(userBaseInfo);
        recallUserInfo.setAddBindIntegral(0);
        recallUserInfo.setAddVipIntegral(0);
        recallUserInfo.setAddVipExp(0);
        recallUserInfo.setRecallTime(System.currentTimeMillis());
        return recallUserInfo;
    }

    /**
     * 同步增加的经验值
     * @param userId
     * @param recallUserId
     * @param addVipExp
     */
    public static void syncAddVipExp(long userId, long recallUserId, int addVipExp) {
        BusinessReunionData businessReunionData = getBusinessReturnData(userId);
        if (businessReunionData == null) {
            return;
        }
        RecallUserInfo recallUserInfo = getRecallUserInfo(businessReunionData, recallUserId);
        if (recallUserInfo == null) {
            return;
        }
        synchronized (getUserLock(userId)) {
            if (recallUserInfo.getAddVipExp() < addVipExp) {
                recallUserInfo.setAddVipExp(addVipExp);
                calAddVipExpAddIntegral(businessReunionData, recallUserInfo);
                businessReunionData.setUpdateOption();
                addIntegral(businessReunionData);
            }
        }
    }

    public static void addIntegral(BusinessReunionData data) {
        synchronized (getUserLock(data.getPlayerId())) {
            GamePlayer onlinePlayer = GamePlayerMgr.getOnlinePlayer(data.getPlayerId());
            if (onlinePlayer == null) {
                return;
            }
            ReunionConfig config = BusinessReunionActivityMgr.getConfig();
            if (config == null) {
                return;
            }
            int recallAddIntegral = 0; //分别算上限
            int addVipExpAddIntegral = 0;
            for (RecallUserInfo recallUserInfo : data.getRecallUserInfos()) {
                recallAddIntegral += recallUserInfo.getAddBindIntegral();
                addVipExpAddIntegral += recallUserInfo.getAddVipIntegral();
            }
            recallAddIntegral = Math.min(config.getRecallAddIntegralLimit(), recallAddIntegral);
            addVipExpAddIntegral = Math.min(config.getVipExpAddIntegralLimit(), addVipExpAddIntegral);

            int canGetIntegral = recallAddIntegral + addVipExpAddIntegral;
            if (canGetIntegral > data.getAlreadyGetIntegral()) {
                int needAddIntegral = canGetIntegral - data.getAlreadyGetIntegral();
                data.setAlreadyGetIntegral(canGetIntegral);
                onlinePlayer.getModule(CurrencyModule.class).addCurrency(new Property(config.getExchangeScoreItemId(), BigInteger.valueOf(needAddIntegral)), eLogMoneyType.BusinessReunion, eLogMoneyType.BusinessReunionSyncAddIntegral);
            }
            onlinePlayer.getModule(BusinessReunionActivityModule.class).syncActivityData();
        }
    }

    private static RecallUserInfo getRecallUserInfo(BusinessReunionData businessReunionData, long recallUserId) {
        long lastRecallTime = 0;
        RecallUserInfo lastRecallUserInfo = null;
        for (RecallUserInfo recallUserInfo : businessReunionData.getRecallUserInfos()) {
            if (recallUserInfo.getRecallUserId() == recallUserId && recallUserInfo.getRecallTime() > lastRecallTime) {
                lastRecallUserInfo = recallUserInfo;
            }
        }
        return lastRecallUserInfo;
    }

    @Override
    public boolean init() throws Exception {
        return reload();
    }

    @Override
    public boolean reloadConfig() throws Exception {
        return true;
    }

    @Override
    public boolean reloadData() throws Exception {
        return true;
    }

    @Override
    public boolean stop() {
        return false;
    }

    @Override
    public boolean save() {
        if (dataMap != null) {
            for (BusinessReunionData reunionData : dataMap.values()) {
                if (reunionData.isInsertOption()) {
                    BusinessReunionBusiness.addData(reunionData);
                } else if (reunionData.isUpdateOption()) {
                    BusinessReunionBusiness.updateData(reunionData);
                }
            }
        }
        return true;
    }

    final static char[] digits = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
            'k', 'l','m', 'n','o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z','A', 'B', 'C', 'D', 'E',
            'F', 'G', 'H', 'I', 'J', 'K', 'L','M', 'N','O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    };

    public static boolean checkStringAllInDigits(String str) {
        if (str.isEmpty()) {
            return false;
        }
        String digitsStr = String.valueOf(digits);
        char[] chars = str.toCharArray();
        for (char aChar : chars) {
            if (digitsStr.indexOf(aChar) == -1) {
                return false;
            }
        }
        return true;
    }

    public static String tenToSixtyTwoHex(long value) {
        StringBuilder res = new StringBuilder();
        while (true) {
            int n = (int) (value % digits.length);
            value = (value - n) / digits.length;
            res.append(digits[n]);
            if (value <= 0) {
                break;
            }
        }
        return res.reverse().toString();
    }

    public static long sixtyTwoToTenHex(String value) {
        long res = 0;
        if (!StringUtils.isNullOrEmpty(value)) {
            char[] chars = org.apache.commons.lang.StringUtils.reverse(value).toCharArray();
            for (int i = 0; i < chars.length; i++) {
                int asc = chars[i];
                int n = 0;
                if (asc >= 48 && asc <= 57) { //0-9
                    n = asc - 48; // 0-9
                } else if (asc >= 97 && asc <= 122) { //a-z
                    n = asc - 87; // 10-35
                } else if (asc >= 65 && asc <= 90) { // A-Z
                    n = asc - 29; // 36-61
                }
                res += (long) (Math.pow(digits.length, i) * n);

            }
        }
        return res;
    }
    public static void main(String[] args) {
        String recallCode = tenToSixtyTwoHex(101000280001569L);
        long userId = BusinessReunionMgr.sixtyTwoToTenHex(recallCode);
        String userIdStr = String.valueOf(userId);
        if (userIdStr.length() != 15) {
            System.out.println("error");
        }
        long serverId = Long.parseLong(userIdStr.substring(0, 8));
        System.out.println("userId:{" + userIdStr + "}");
        System.out.println("serverId:{" + serverId + "}");
    }
}
